From 1b05fa170fbb9c59ca0aed0679a1908734a4df1e Mon Sep 17 00:00:00 2001 From: dankelleher Date: Tue, 7 Mar 2023 09:27:19 +0100 Subject: [PATCH 01/12] Fix extract script --- packages/scripts/extract.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/scripts/extract.ts b/packages/scripts/extract.ts index 33767ed8..dbad60a8 100644 --- a/packages/scripts/extract.ts +++ b/packages/scripts/extract.ts @@ -1,7 +1,7 @@ -import { SunriseStakeClient } from "../app/src/lib/client/"; +import { SunriseStakeClient } from "../client/src"; import "./util"; import { AnchorProvider } from "@project-serum/anchor"; -import { SUNRISE_STAKE_STATE } from "@sunrisestake/app/src/lib/constants"; +import { SUNRISE_STAKE_STATE } from "../client/src/constants"; (async () => { const provider = AnchorProvider.env(); From 2cb95ff8d7b00e5ab3ce451ee3e810c97643dc5f Mon Sep 17 00:00:00 2001 From: dankelleher Date: Tue, 7 Mar 2023 09:32:00 +0100 Subject: [PATCH 02/12] Add a price lookup for SOL and USD. --- packages/app/src/lib/util.ts | 51 +++++++++++++++++++++++++-------- packages/tests/sunrise-stake.ts | 2 +- 2 files changed, 40 insertions(+), 13 deletions(-) diff --git a/packages/app/src/lib/util.ts b/packages/app/src/lib/util.ts index 9a0a85e5..5468cc71 100644 --- a/packages/app/src/lib/util.ts +++ b/packages/app/src/lib/util.ts @@ -2,11 +2,11 @@ import { LAMPORTS_PER_SOL, type PublicKey } from "@solana/web3.js"; import { type SignerWalletAdapterProps } from "@solana/wallet-adapter-base"; import BN from "bn.js"; -export const ZERO = new BN(0); +const ZERO = new BN(0); -export const toBN = (n: number): BN => new BN(`${n}`); +const toBN = (n: number): BN => new BN(`${n}`); -export const walletIsConnected = ( +const walletIsConnected = ( wallet: SparseWalletContextAdapter ): wallet is ConnectedWallet => wallet.connected && wallet.publicKey != null; @@ -16,7 +16,7 @@ export const toSol = (lamports: BN, precision = MAX_NUM_PRECISION): number => lamports.div(new BN(10).pow(new BN(precision))).toNumber() / (LAMPORTS_PER_SOL / 10 ** precision); -export const solToLamports = (sol: number | string): BN => { +const solToLamports = (sol: number | string): BN => { // handle very big numbers but also integers. // note this doesn't handle large numbers with decimals. // in other words, if you ask for eg a withdrawal of 1e20 SOL + 0.1 SOL, it will round that to 1e20 SOL.TODO fix this later. @@ -39,12 +39,12 @@ const formatPrecision = (n: number, precision = MAX_NUM_PRECISION): number => precision ); -export const toFixedWithPrecision = ( +const toFixedWithPrecision = ( n: number, precision = MAX_NUM_PRECISION ): string => n.toFixed(formatPrecision(n, precision)); -export const getDigits = (strNo: string): number | undefined => { +const getDigits = (strNo: string): number | undefined => { const match = strNo.match(/^\d*\.(\d+)$/); if (match?.[1] != null) return match[1].length; }; @@ -62,14 +62,30 @@ type SparseWalletContextAdapter = Omit & { export type ConnectedWallet = SparseWallet & { connected: true }; -// TODO TEMP lookup -export const SOL_PRICE_USD_CENTS = 2500; -export const CARBON_PRICE_USD_CENTS_PER_TONNE = 173; // NCT price in USD cents +const DEFAULT_SOLANA_USD_PRICE = 2000; // SOL price in USD cents +const DEFAULT_NCT_USD_PRICE = 200; // NCT price in USD cents -export const solToCarbon = (sol: number): number => - (sol * SOL_PRICE_USD_CENTS) / CARBON_PRICE_USD_CENTS_PER_TONNE; +interface Prices { + solana: number; + nct: number; +} +const PRICES: Prices = { + solana: DEFAULT_SOLANA_USD_PRICE, + nct: DEFAULT_NCT_USD_PRICE, +}; + +fetch("https://api.sunrisestake.com/prices") + .then(async (res) => res.json()) + .then(({ solana, "toucan-protocol-nature-carbon-tonne": nct }) => { + console.log("Prices", { solana, nct }); + PRICES.solana = Number(solana.usd) * 100; + PRICES.nct = Number(nct.usd) * 100; + }) + .catch(console.error); -export function debounce) => ReturnType>( +const solToCarbon = (sol: number): number => (sol * PRICES.solana) / PRICES.nct; + +function debounce) => ReturnType>( func: F, waitFor: number ): (...args: Parameters) => void { @@ -79,3 +95,14 @@ export function debounce) => ReturnType>( timeout = setTimeout(() => func(...args), waitFor); }; } + +export { + ZERO, + debounce, + getDigits, + solToCarbon, + solToLamports, + toBN, + toFixedWithPrecision, + walletIsConnected, +}; diff --git a/packages/tests/sunrise-stake.ts b/packages/tests/sunrise-stake.ts index eb7ef3eb..933d8dc4 100644 --- a/packages/tests/sunrise-stake.ts +++ b/packages/tests/sunrise-stake.ts @@ -208,7 +208,7 @@ describe("sunrise-stake", () => { // So after the previous deposit, the liquidity pool is at its minimum (5%) // Any subsequent liquid unstakes will trigger a rebalance // So to avoid this (in order to test feeless unstake) - // we deposit 10 more here, which shoudl go straight into the LP, bringing it back up to its maximum (10%) + // we deposit 10 more here, which should go straight into the LP, bringing it back up to its maximum (10%) it("deposits into the liquidity pool to rebalance the pools", async () => { await getBalance(client); // print balance before deposit From ecb5df16eeb4dae42f57a65315eba0ed5924cae7 Mon Sep 17 00:00:00 2001 From: dankelleher Date: Tue, 7 Mar 2023 09:39:54 +0100 Subject: [PATCH 03/12] Update to use yield controller total tokens purchased instead of holding amount in useCarbon. (allowing SOL to be removed from the holding amount without affecting historical values) --- packages/app/package.json | 1 + packages/app/src/hooks/useCarbon.ts | 37 ++++++++++++-------- packages/app/src/hooks/useYieldController.ts | 23 ++++++++++++ packages/app/src/lib/sunriseClientWrapper.ts | 4 +++ packages/app/src/utils/tooltips.tsx | 2 +- packages/client/src/constants.ts | 27 ++++++++++++++ yarn.lock | 31 +++++++++++++++- 7 files changed, 109 insertions(+), 16 deletions(-) create mode 100644 packages/app/src/hooks/useYieldController.ts diff --git a/packages/app/package.json b/packages/app/package.json index b68cac88..09950cfa 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -15,6 +15,7 @@ "@solana/web3.js": "^1.66.2", "@sunrisestake/client": "0.1.0", "@sunrisestake/marinade-ts-sdk": "4.0.4-alpha.18", + "@sunrisestake/yield-controller": "^0.0.3", "@tailwindcss/forms": "^0.5.3", "@tailwindcss/typography": "^0.5.7", "@testing-library/jest-dom": "^5.16.5", diff --git a/packages/app/src/hooks/useCarbon.ts b/packages/app/src/hooks/useCarbon.ts index 81c6565a..70598022 100644 --- a/packages/app/src/hooks/useCarbon.ts +++ b/packages/app/src/hooks/useCarbon.ts @@ -1,13 +1,12 @@ -import { useSunriseStake } from "../context/sunriseStakeContext"; -import { useEffect, useState } from "react"; import BN from "bn.js"; +import { useEffect, useState } from "react"; import { solToCarbon, toSol } from "../lib/util"; -import { useConnection } from "@solana/wallet-adapter-react"; -import { HOLDING_ACCOUNT } from "@sunrisestake/client"; +import { useSunriseStake } from "../context/sunriseStakeContext"; +import { useYieldController } from "./useYieldController"; -export const useCarbon = (): { totalCarbon: number | undefined } => { - const { connection } = useConnection(); +const useCarbon = (): { totalCarbon: number | undefined } => { const { details } = useSunriseStake(); + const yieldControllerState = useYieldController(); const [totalCarbon, setTotalCarbon] = useState(); useEffect(() => { @@ -17,26 +16,34 @@ export const useCarbon = (): { totalCarbon: number | undefined } => { // Total carbon is the carbon value of // 1. the extractable yield // 2. the treasury balance - // 3. the holding account balance - // (TODO this last one will be replaced with the TreasuryController total_spent value) + // 3. the YieldController totalTokensPurchased value const extractableYield = details.extractableYield; const treasuryBalance = new BN(details.balances.treasuryBalance); + // no longer used to calculate the amount - can be removed const holdingAccountBalance = new BN( - await connection.getBalance(HOLDING_ACCOUNT) + details.balances.holdingAccountBalance ); + // this is the amount of carbon tokens burned so far by the protocol + const totalTokensPurchased = + yieldControllerState?.totalTokensPurchased ?? new BN(0); + + // this is the current price set in the yield controller for buying carbon tokens + // TODO use this instead of the hard-coded values to convert SOL to Carbon + // const price = yieldControllerState?.price ?? 0; - const totalLamports = extractableYield - .add(treasuryBalance) - .add(holdingAccountBalance); + const totalLamportsWaiting = extractableYield.add(treasuryBalance); - const carbon = solToCarbon(toSol(totalLamports)); + const carbon = + solToCarbon(toSol(totalLamportsWaiting)) + + totalTokensPurchased.toNumber(); console.log({ extractableYield: toSol(extractableYield), treasuryBalance: toSol(treasuryBalance), holdingAccountBalance: toSol(holdingAccountBalance), - totalLamports: toSol(totalLamports), + totalLamportsWaiting: toSol(totalLamportsWaiting), + totalTokensPurchased: totalTokensPurchased.toNumber(), totalCarbon: carbon, }); @@ -49,3 +56,5 @@ export const useCarbon = (): { totalCarbon: number | undefined } => { return { totalCarbon }; }; + +export { useCarbon }; diff --git a/packages/app/src/hooks/useYieldController.ts b/packages/app/src/hooks/useYieldController.ts new file mode 100644 index 00000000..a38035cc --- /dev/null +++ b/packages/app/src/hooks/useYieldController.ts @@ -0,0 +1,23 @@ +import { useSunriseStake } from "../context/sunriseStakeContext"; +import { useEffect, useState } from "react"; +import { + type YieldControllerState, + YieldControllerClient, +} from "@sunrisestake/yield-controller"; + +export const useYieldController = (): YieldControllerState | undefined => { + const { client } = useSunriseStake(); + const [yieldState, setYieldState] = useState(); + useEffect(() => { + void (async () => { + if (!client) return; + const yieldControllerClient = await YieldControllerClient.get( + client.internal().provider, + client.yieldControllerState + ); + yieldControllerClient.getState().then(setYieldState).catch(console.error); + })(); + }, [client?.yieldControllerState]); + + return yieldState; +}; diff --git a/packages/app/src/lib/sunriseClientWrapper.ts b/packages/app/src/lib/sunriseClientWrapper.ts index 76fba0d1..2b282575 100644 --- a/packages/app/src/lib/sunriseClientWrapper.ts +++ b/packages/app/src/lib/sunriseClientWrapper.ts @@ -124,4 +124,8 @@ export class SunriseClientWrapper { .sendAndConfirm(tx, []) .then(this.triggerUpdateAndReturn.bind(this)); } + + get yieldControllerState(): PublicKey { + return this.client.env.yieldControllerState; + } } diff --git a/packages/app/src/utils/tooltips.tsx b/packages/app/src/utils/tooltips.tsx index 2012fcaf..4eb3f95a 100644 --- a/packages/app/src/utils/tooltips.tsx +++ b/packages/app/src/utils/tooltips.tsx @@ -3,7 +3,7 @@ export const tooltips = { totalStake: <>The sum of everyones staked SOL, offsetCO2: ( <> - Tonnes of Carbon Dioxide or equivalent (tCO2e) offset by Sunrise so far. + Tonnes of Carbon Dioxide or equivalent (tCO₂E) offset by Sunrise so far. Note: This number includes yield that Sunrise has accrued, but not yet spent diff --git a/packages/client/src/constants.ts b/packages/client/src/constants.ts index 1defe797..4e47bdbd 100644 --- a/packages/client/src/constants.ts +++ b/packages/client/src/constants.ts @@ -13,6 +13,7 @@ interface BlazeConfig { interface EnvironmentConfig { state: PublicKey; holdingAccount: PublicKey; + yieldControllerState: PublicKey; percentageStakeToMarinade: number; blaze: BlazeConfig; } @@ -22,6 +23,9 @@ export const Environment: Record = { holdingAccount: new PublicKey( "shcFT8Ur2mzpX61uWQRL9KyERZp4w2ehDEvA7iaAthn" ), + yieldControllerState: new PublicKey( + "htGs6L3pCRxgfkJP2vLUdb9hVPtcE4mKsdWP4CnirQA" + ), percentageStakeToMarinade: 200, // TODO TEMP fix blaze: { pool: new PublicKey("stk9ApL5HeVAwPLr3TLhDXdZS8ptVu7zp6ov8HFDuMi"), @@ -33,6 +37,9 @@ export const Environment: Record = { state: new PublicKey("DR3hrjH6SZefraRu8vaQfEhG5e6E25ZwccakQxWRePkC"), // Warning obsolete holdingAccount: PublicKey.default, percentageStakeToMarinade: 75, + yieldControllerState: new PublicKey( + "77aJfgRudbv9gFfjRQw3tuYzgnjoDgs9jorVTmK7cv73" + ), blaze: { pool: PublicKey.default, bsolMint: PublicKey.default, @@ -43,6 +50,26 @@ export const Environment: Record = { holdingAccount: new PublicKey( "dhcB568T3skiP2D9ujf4eAJEnW2gACaaA9BUCVbwbXD" ), + yieldControllerState: new PublicKey( + "77aJfgRudbv9gFfjRQw3tuYzgnjoDgs9jorVTmK7cv73" + ), + yieldControllerState: new PublicKey( + "77aJfgRudbv9gFfjRQw3tuYzgnjoDgs9jorVTmK7cv73" + ), + percentageStakeToMarinade: 75, + blaze: { + pool: new PublicKey("azFVdHtAJN8BX3sbGAYkXvtdjdrT5U6rj9rovvUFos9"), + bsolMint: new PublicKey("bSo13r4TkiE4KumL71LsHTPpL2euBYLFx6h9HP3piy1"), + }, + }, + localnet: { + state: new PublicKey("28SkW4iD7UJc9zkxcq6yNb1MFX2hxqdJjxjZs67Jwr2b"), + holdingAccount: new PublicKey( + "dhcB568T3skiP2D9ujf4eAJEnW2gACaaA9BUCVbwbXD" + ), + yieldControllerState: new PublicKey( + "77aJfgRudbv9gFfjRQw3tuYzgnjoDgs9jorVTmK7cv73" + ), percentageStakeToMarinade: 75, blaze: { pool: new PublicKey("azFVdHtAJN8BX3sbGAYkXvtdjdrT5U6rj9rovvUFos9"), diff --git a/yarn.lock b/yarn.lock index 1cd4bf3c..9853265f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1125,6 +1125,27 @@ eventemitter3 "^4.0.7" uuid "^8.3.2" +"@coral-xyz/anchor@^0.26.0": + version "0.26.0" + resolved "https://registry.yarnpkg.com/@coral-xyz/anchor/-/anchor-0.26.0.tgz#c8e4f7177e93441afd030f22d777d54d0194d7d1" + integrity sha512-PxRl+wu5YyptWiR9F2MBHOLLibm87Z4IMUBPreX+DYBtPM+xggvcPi0KAN7+kIL4IrIhXI8ma5V0MCXxSN1pHg== + dependencies: + "@coral-xyz/borsh" "^0.26.0" + "@solana/web3.js" "^1.68.0" + base64-js "^1.5.1" + bn.js "^5.1.2" + bs58 "^4.0.1" + buffer-layout "^1.2.2" + camelcase "^6.3.0" + cross-fetch "^3.1.5" + crypto-hash "^1.3.0" + eventemitter3 "^4.0.7" + js-sha256 "^0.9.0" + pako "^2.0.3" + snake-case "^3.0.4" + superstruct "^0.15.4" + toml "^3.0.0" + "@coral-xyz/borsh@^0.26.0": version "0.26.0" resolved "https://registry.yarnpkg.com/@coral-xyz/borsh/-/borsh-0.26.0.tgz#d054f64536d824634969e74138f9f7c52bbbc0d5" @@ -3519,7 +3540,7 @@ dependencies: buffer "~6.0.3" -"@solana/spl-token@^0.3.5", "@solana/spl-token@^0.3.6": +"@solana/spl-token@^0.3.5", "@solana/spl-token@^0.3.6", "@solana/spl-token@^0.3.7": version "0.3.7" resolved "https://registry.yarnpkg.com/@solana/spl-token/-/spl-token-0.3.7.tgz#6f027f9ad8e841f792c32e50920d9d2e714fc8da" integrity sha512-bKGxWTtIw6VDdCBngjtsGlKGLSmiu/8ghSt/IOYJV24BsymRbgq7r12GToeetpxmPaZYLddKwAz7+EwprLfkfg== @@ -4180,6 +4201,14 @@ borsh "^0.6.0" bs58 "^5.0.0" +"@sunrisestake/yield-controller@^0.0.3": + version "0.0.3" + resolved "https://registry.yarnpkg.com/@sunrisestake/yield-controller/-/yield-controller-0.0.3.tgz#f49b334126e9e3e26dfbea7ac107d56146a5b89d" + integrity sha512-IQOSzrsZsR7QTiDamXZOP/FRoxJMulDSJ9boA4qd054/OzhtSKy6TQD2/oOX6Grp3garMk5REu594+XD65GejA== + dependencies: + "@coral-xyz/anchor" "^0.26.0" + "@solana/spl-token" "^0.3.7" + "@supercharge/promise-pool@^2.1.0": version "2.3.2" resolved "https://registry.yarnpkg.com/@supercharge/promise-pool/-/promise-pool-2.3.2.tgz#6366894a7e7bc699bb65e58d8c828113729cf481" From f69a5813db0519adfc6476b25546eac56f9fe2e0 Mon Sep 17 00:00:00 2001 From: dankelleher Date: Mon, 6 Mar 2023 17:46:28 +0100 Subject: [PATCH 04/12] Post-cherry-pick minimal changes to allow the old UI to work against the new program --- packages/app/src/hooks/useCarbon.ts | 8 ++--- packages/app/src/hooks/useYieldController.ts | 7 +++-- packages/app/src/lib/sunriseClientWrapper.ts | 6 +--- packages/client/src/constants.ts | 19 ++---------- packages/client/src/marinade.ts | 31 ++++++++++++-------- 5 files changed, 29 insertions(+), 42 deletions(-) diff --git a/packages/app/src/hooks/useCarbon.ts b/packages/app/src/hooks/useCarbon.ts index 70598022..1a701907 100644 --- a/packages/app/src/hooks/useCarbon.ts +++ b/packages/app/src/hooks/useCarbon.ts @@ -21,9 +21,9 @@ const useCarbon = (): { totalCarbon: number | undefined } => { const extractableYield = details.extractableYield; const treasuryBalance = new BN(details.balances.treasuryBalance); // no longer used to calculate the amount - can be removed - const holdingAccountBalance = new BN( - details.balances.holdingAccountBalance - ); + // const holdingAccountBalance = new BN( + // details.balances.holdingAccountBalance + // ); // this is the amount of carbon tokens burned so far by the protocol const totalTokensPurchased = yieldControllerState?.totalTokensPurchased ?? new BN(0); @@ -41,7 +41,7 @@ const useCarbon = (): { totalCarbon: number | undefined } => { console.log({ extractableYield: toSol(extractableYield), treasuryBalance: toSol(treasuryBalance), - holdingAccountBalance: toSol(holdingAccountBalance), + // holdingAccountBalance: toSol(holdingAccountBalance), totalLamportsWaiting: toSol(totalLamportsWaiting), totalTokensPurchased: totalTokensPurchased.toNumber(), totalCarbon: carbon, diff --git a/packages/app/src/hooks/useYieldController.ts b/packages/app/src/hooks/useYieldController.ts index a38035cc..ff9a22fb 100644 --- a/packages/app/src/hooks/useYieldController.ts +++ b/packages/app/src/hooks/useYieldController.ts @@ -4,6 +4,7 @@ import { type YieldControllerState, YieldControllerClient, } from "@sunrisestake/yield-controller"; +import { YIELD_CONTROLLER_STATE } from "@sunrisestake/client"; export const useYieldController = (): YieldControllerState | undefined => { const { client } = useSunriseStake(); @@ -12,12 +13,12 @@ export const useYieldController = (): YieldControllerState | undefined => { void (async () => { if (!client) return; const yieldControllerClient = await YieldControllerClient.get( - client.internal().provider, - client.yieldControllerState + client.client.provider, + YIELD_CONTROLLER_STATE ); yieldControllerClient.getState().then(setYieldState).catch(console.error); })(); - }, [client?.yieldControllerState]); + }, [YIELD_CONTROLLER_STATE]); return yieldState; }; diff --git a/packages/app/src/lib/sunriseClientWrapper.ts b/packages/app/src/lib/sunriseClientWrapper.ts index 2b282575..a3dd6f6e 100644 --- a/packages/app/src/lib/sunriseClientWrapper.ts +++ b/packages/app/src/lib/sunriseClientWrapper.ts @@ -14,7 +14,7 @@ import { debounce } from "./util"; export class SunriseClientWrapper { public debouncedUpdate = debounce(this.triggerUpdate.bind(this), 1000); constructor( - private readonly client: SunriseStakeClient, + readonly client: SunriseStakeClient, private readonly detailsListener: ((details: Details) => void) | undefined, readonly readonlyWallet: boolean ) { @@ -124,8 +124,4 @@ export class SunriseClientWrapper { .sendAndConfirm(tx, []) .then(this.triggerUpdateAndReturn.bind(this)); } - - get yieldControllerState(): PublicKey { - return this.client.env.yieldControllerState; - } } diff --git a/packages/client/src/constants.ts b/packages/client/src/constants.ts index 4e47bdbd..cbcac7c8 100644 --- a/packages/client/src/constants.ts +++ b/packages/client/src/constants.ts @@ -50,9 +50,6 @@ export const Environment: Record = { holdingAccount: new PublicKey( "dhcB568T3skiP2D9ujf4eAJEnW2gACaaA9BUCVbwbXD" ), - yieldControllerState: new PublicKey( - "77aJfgRudbv9gFfjRQw3tuYzgnjoDgs9jorVTmK7cv73" - ), yieldControllerState: new PublicKey( "77aJfgRudbv9gFfjRQw3tuYzgnjoDgs9jorVTmK7cv73" ), @@ -62,20 +59,6 @@ export const Environment: Record = { bsolMint: new PublicKey("bSo13r4TkiE4KumL71LsHTPpL2euBYLFx6h9HP3piy1"), }, }, - localnet: { - state: new PublicKey("28SkW4iD7UJc9zkxcq6yNb1MFX2hxqdJjxjZs67Jwr2b"), - holdingAccount: new PublicKey( - "dhcB568T3skiP2D9ujf4eAJEnW2gACaaA9BUCVbwbXD" - ), - yieldControllerState: new PublicKey( - "77aJfgRudbv9gFfjRQw3tuYzgnjoDgs9jorVTmK7cv73" - ), - percentageStakeToMarinade: 75, - blaze: { - pool: new PublicKey("azFVdHtAJN8BX3sbGAYkXvtdjdrT5U6rj9rovvUFos9"), - bsolMint: new PublicKey("bSo13r4TkiE4KumL71LsHTPpL2euBYLFx6h9HP3piy1"), - }, - }, }; const ActiveEnvironment = @@ -90,6 +73,8 @@ export const SUNRISE_STAKE_STATE = ActiveEnvironment.state; export const HOLDING_ACCOUNT = ActiveEnvironment.holdingAccount; +export const YIELD_CONTROLLER_STATE = ActiveEnvironment.yieldControllerState; + export const DEFAULT_LP_PROPORTION = 10; export const DEFAULT_LP_MIN_PROPORTION = 5; diff --git a/packages/client/src/marinade.ts b/packages/client/src/marinade.ts index 72bad69e..efcd21df 100644 --- a/packages/client/src/marinade.ts +++ b/packages/client/src/marinade.ts @@ -247,20 +247,25 @@ export const liquidUnstake = async ( marinadeProgram, }; - const { instruction: rebalanceInstruction } = await triggerRebalance( - config, - marinade, - marinadeState, - program, - stateAddress, - staker - ); + // Disable triggerRebalance - this allows the old client (with minimal additional changes) + // to be used against the new program - return program.methods - .liquidUnstake(lamports) - .accounts(accounts) - .postInstructions([rebalanceInstruction]) - .transaction(); + // const { instruction: rebalanceInstruction } = await triggerRebalance( + // config, + // marinade, + // marinadeState, + // program, + // stateAddress, + // staker + // ); + + return ( + program.methods + .liquidUnstake(lamports) + .accounts(accounts) + // .postInstructions([rebalanceInstruction]) + .transaction() + ); }; export const orders = async ( From b812bc5c43aede6de3b4d1f4d44db8f38c012856 Mon Sep 17 00:00:00 2001 From: dankelleher Date: Tue, 7 Mar 2023 10:14:01 +0100 Subject: [PATCH 05/12] some price logging --- packages/app/src/hooks/useCarbon.ts | 3 ++- packages/app/src/lib/util.ts | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/app/src/hooks/useCarbon.ts b/packages/app/src/hooks/useCarbon.ts index 1a701907..233ca131 100644 --- a/packages/app/src/hooks/useCarbon.ts +++ b/packages/app/src/hooks/useCarbon.ts @@ -1,6 +1,6 @@ import BN from "bn.js"; import { useEffect, useState } from "react"; -import { solToCarbon, toSol } from "../lib/util"; +import { PRICES, solToCarbon, toSol } from "../lib/util"; import { useSunriseStake } from "../context/sunriseStakeContext"; import { useYieldController } from "./useYieldController"; @@ -45,6 +45,7 @@ const useCarbon = (): { totalCarbon: number | undefined } => { totalLamportsWaiting: toSol(totalLamportsWaiting), totalTokensPurchased: totalTokensPurchased.toNumber(), totalCarbon: carbon, + prices: PRICES, }); // due to fees, at low values, the total can be negative diff --git a/packages/app/src/lib/util.ts b/packages/app/src/lib/util.ts index 5468cc71..acdd0ce3 100644 --- a/packages/app/src/lib/util.ts +++ b/packages/app/src/lib/util.ts @@ -69,7 +69,7 @@ interface Prices { solana: number; nct: number; } -const PRICES: Prices = { +export const PRICES: Prices = { solana: DEFAULT_SOLANA_USD_PRICE, nct: DEFAULT_NCT_USD_PRICE, }; From d15ac54455f4b69a986f8ea8a96c4283c64acfb2 Mon Sep 17 00:00:00 2001 From: dankelleher Date: Tue, 7 Mar 2023 14:18:46 +0100 Subject: [PATCH 06/12] Fix co2 reporting --- packages/app/src/hooks/useCarbon.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/app/src/hooks/useCarbon.ts b/packages/app/src/hooks/useCarbon.ts index 233ca131..47ab2a3e 100644 --- a/packages/app/src/hooks/useCarbon.ts +++ b/packages/app/src/hooks/useCarbon.ts @@ -26,7 +26,8 @@ const useCarbon = (): { totalCarbon: number | undefined } => { // ); // this is the amount of carbon tokens burned so far by the protocol const totalTokensPurchased = - yieldControllerState?.totalTokensPurchased ?? new BN(0); + (yieldControllerState?.totalTokensPurchased ?? new BN(0)).toNumber() / + 10 ** 8; // tokens purchased are stored on-chain in minor denomination // this is the current price set in the yield controller for buying carbon tokens // TODO use this instead of the hard-coded values to convert SOL to Carbon @@ -35,15 +36,14 @@ const useCarbon = (): { totalCarbon: number | undefined } => { const totalLamportsWaiting = extractableYield.add(treasuryBalance); const carbon = - solToCarbon(toSol(totalLamportsWaiting)) + - totalTokensPurchased.toNumber(); + solToCarbon(toSol(totalLamportsWaiting)) + totalTokensPurchased; console.log({ extractableYield: toSol(extractableYield), treasuryBalance: toSol(treasuryBalance), // holdingAccountBalance: toSol(holdingAccountBalance), totalLamportsWaiting: toSol(totalLamportsWaiting), - totalTokensPurchased: totalTokensPurchased.toNumber(), + totalTokensPurchased, totalCarbon: carbon, prices: PRICES, }); @@ -53,7 +53,7 @@ const useCarbon = (): { totalCarbon: number | undefined } => { setTotalCarbon(normalizedCarbon); })(); - }, [details]); + }, [details, yieldControllerState]); return { totalCarbon }; }; From db4343b790dac99349d9c1f4012801398286626c Mon Sep 17 00:00:00 2001 From: dankelleher Date: Wed, 8 Mar 2023 11:54:51 +0100 Subject: [PATCH 07/12] Fix min lp proportion --- packages/client/src/index.ts | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/packages/client/src/index.ts b/packages/client/src/index.ts index 403b4f5d..4b70dc37 100644 --- a/packages/client/src/index.ts +++ b/packages/client/src/index.ts @@ -855,7 +855,7 @@ export class SunriseStakeClient { const lpSolShare = details.lpDetails.lpSolShare; const preferredMinLiqPoolValue = new BN( details.balances.gsolSupply.amount - ).muln(0.1); + ).muln(DEFAULT_LP_MIN_PROPORTION).divn(100); const postUnstakeLpSolValue = new BN(lpSolShare).sub(withdrawalLamports); // Calculate how much will be withdrawn through liquid unstaking (with fee) @@ -870,12 +870,17 @@ export class SunriseStakeClient { ? MARINADE_TICKET_RENT : 0; - this.log("amount to order unstake: ", amountToOrderUnstake); - this.log("rent for order unstake: ", rentForOrderUnstakeTicket); + this.log("withdrawal lamports: ", withdrawalLamports.toString()); + this.log("lp sol share: ", lpSolShare.toString()); + this.log("preferred min lp value: ", preferredMinLiqPoolValue.toString()); + this.log("post unstake lp sol value: ", postUnstakeLpSolValue.toString()); + this.log("amount being liquid unstaked: ", amountBeingLiquidUnstaked.toString()); + this.log("amount to order unstake: ", amountToOrderUnstake.toString()); + this.log("rent for order unstake: ", rentForOrderUnstakeTicket.toString()); const ticketFee = rentForOrderUnstakeTicket; - let totalFee = new BN(rentForOrderUnstakeTicket + 2 * NETWORK_FEE); + let totalFee = rentForOrderUnstakeTicket > 0 ? new BN(rentForOrderUnstakeTicket + 2 * NETWORK_FEE) : ZERO; if (amountBeingLiquidUnstaked.lte(ZERO)) { return { From b4e68858e6a60af7ee229ee31aedb08fc7ecb87c Mon Sep 17 00:00:00 2001 From: dankelleher Date: Sat, 11 Mar 2023 10:47:47 +0100 Subject: [PATCH 08/12] Dummy update to retrigger vercel deploy with new environment variables --- packages/app/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/app/README.md b/packages/app/README.md index 7e2decf5..6c172c4a 100644 --- a/packages/app/README.md +++ b/packages/app/README.md @@ -1,5 +1,7 @@ # Sunrise Stake App +(pre-trees version) + The UI for [Sunrise Stake](https://app.sunrisestake.com) ## Getting started From 174b61a60621c18f388148f103b8e44c1b87b90c Mon Sep 17 00:00:00 2001 From: dankelleher Date: Fri, 10 Mar 2023 08:12:19 +0100 Subject: [PATCH 09/12] Add flag to disable solblaze due to issues in devnet --- packages/client/src/constants.ts | 3 +++ packages/client/src/index.ts | 6 +++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/client/src/constants.ts b/packages/client/src/constants.ts index 0a7ac8bc..ac95d60e 100644 --- a/packages/client/src/constants.ts +++ b/packages/client/src/constants.ts @@ -3,6 +3,9 @@ import { PublicKey } from "@solana/web3.js"; import { type EpochReportAccount } from "./types/EpochReportAccount"; import BN from "bn.js"; +// on devnet, the solblaze pool is sometimes unavailable for deposits - use this to disable it +export const SOLBLAZE_ENABLED = false; + export const STAKE_POOL_PROGRAM_ID = new PublicKey( "SPoo1Ku8WFXoNDMHPsrGSTSG1Y47rzgn41SLUNakuHy" ); diff --git a/packages/client/src/index.ts b/packages/client/src/index.ts index 4b70dc37..3b64996f 100644 --- a/packages/client/src/index.ts +++ b/packages/client/src/index.ts @@ -54,6 +54,7 @@ import { type EnvironmentConfig, MARINADE_TICKET_RENT, NETWORK_FEE, + SOLBLAZE_ENABLED, STAKE_POOL_PROGRAM_ID, } from "./constants"; import { @@ -240,7 +241,10 @@ export class SunriseStakeClient { public async makeBalancedDeposit(lamports: BN): Promise { const details = await this.details(); - if (marinadeTargetReached(details, this.env.percentageStakeToMarinade)) { + if ( + marinadeTargetReached(details, this.env.percentageStakeToMarinade) && + SOLBLAZE_ENABLED + ) { console.log("Routing deposit to Solblaze"); return this.depositToBlaze(lamports); } From 4f1c26ba58924ec0651bb17a67ad24adfae9546a Mon Sep 17 00:00:00 2001 From: dankelleher Date: Sun, 12 Mar 2023 07:50:54 +0100 Subject: [PATCH 10/12] Trigger app deployment --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index b812e898..5201f000 100644 --- a/README.md +++ b/README.md @@ -149,3 +149,4 @@ yarn test | NCT Mint | [7sbtAMfAuSfsUvZKPWiXUXaizYCnpLL2BBnKNTU3wjfT](https://explorer.solana.com/address/7sbtAMfAuSfsUvZKPWiXUXaizYCnpLL2BBnKNTU3wjfT) | [tnct1RC5jg94CJLpiTZc2A2d98MP1Civjh7o6ShmTP6](https://explorer.solana.com/address/tnct1RC5jg94CJLpiTZc2A2d98MP1Civjh7o6ShmTP6?cluster=devnet) | Carbon token bought and burned by the treasury controller: [Toucan NCT](https://blog.toucan.earth/announcing-nct-nature-carbon-tonne/) - bridged via Wormhole from Polygon. | | | Holding Account SOL | [shcFT8Ur2mzpX61uWQRL9KyERZp4w2ehDEvA7iaAthn](https://explorer.solana.com/address/shcFT8Ur2mzpX61uWQRL9KyERZp4w2ehDEvA7iaAthn) | [dhcB568T3skiP2D9ujf4eAJEnW2gACaaA9BUCVbwbXD](https://explorer.solana.com/address/dhcB568T3skiP2D9ujf4eAJEnW2gACaaA9BUCVbwbXD?cluster=devnet) | Recipient account for SOL used to purchase NCT. (Temporary, until a liquid market for NCT exists on Solana) | | | Holding Account NCT | [9tGKhW8WGkmx1tkxLoMwanb3XgQ9yJFDPnNggYjb1KUR](https://explorer.solana.com/address/9tGKhW8WGkmx1tkxLoMwanb3XgQ9yJFDPnNggYjb1KUR) | [8JGR8UdjLxduLpkn57H3MuoNapFovefXvMZ7k4dNM2a2](https://explorer.solana.com/address/8JGR8UdjLxduLpkn57H3MuoNapFovefXvMZ7k4dNM2a2?cluster=devnet) | NCT account made available to the Treasury Controller to purchase from. TreasuryController state account is a delegate. (Temporary, until a liquid market for NCT exists on Solana) | | + From 99b4383424e38ff5f7793779a82fa9f989246cb0 Mon Sep 17 00:00:00 2001 From: dankelleher Date: Sun, 12 Mar 2023 07:54:13 +0100 Subject: [PATCH 11/12] Trigger build --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 5201f000..b812e898 100644 --- a/README.md +++ b/README.md @@ -149,4 +149,3 @@ yarn test | NCT Mint | [7sbtAMfAuSfsUvZKPWiXUXaizYCnpLL2BBnKNTU3wjfT](https://explorer.solana.com/address/7sbtAMfAuSfsUvZKPWiXUXaizYCnpLL2BBnKNTU3wjfT) | [tnct1RC5jg94CJLpiTZc2A2d98MP1Civjh7o6ShmTP6](https://explorer.solana.com/address/tnct1RC5jg94CJLpiTZc2A2d98MP1Civjh7o6ShmTP6?cluster=devnet) | Carbon token bought and burned by the treasury controller: [Toucan NCT](https://blog.toucan.earth/announcing-nct-nature-carbon-tonne/) - bridged via Wormhole from Polygon. | | | Holding Account SOL | [shcFT8Ur2mzpX61uWQRL9KyERZp4w2ehDEvA7iaAthn](https://explorer.solana.com/address/shcFT8Ur2mzpX61uWQRL9KyERZp4w2ehDEvA7iaAthn) | [dhcB568T3skiP2D9ujf4eAJEnW2gACaaA9BUCVbwbXD](https://explorer.solana.com/address/dhcB568T3skiP2D9ujf4eAJEnW2gACaaA9BUCVbwbXD?cluster=devnet) | Recipient account for SOL used to purchase NCT. (Temporary, until a liquid market for NCT exists on Solana) | | | Holding Account NCT | [9tGKhW8WGkmx1tkxLoMwanb3XgQ9yJFDPnNggYjb1KUR](https://explorer.solana.com/address/9tGKhW8WGkmx1tkxLoMwanb3XgQ9yJFDPnNggYjb1KUR) | [8JGR8UdjLxduLpkn57H3MuoNapFovefXvMZ7k4dNM2a2](https://explorer.solana.com/address/8JGR8UdjLxduLpkn57H3MuoNapFovefXvMZ7k4dNM2a2?cluster=devnet) | NCT account made available to the Treasury Controller to purchase from. TreasuryController state account is a delegate. (Temporary, until a liquid market for NCT exists on Solana) | | - From a28c09166160dee0fbcf480a8da9af91fff2c6d6 Mon Sep 17 00:00:00 2001 From: dankelleher Date: Sun, 12 Mar 2023 09:30:17 +0100 Subject: [PATCH 12/12] Fix for missing yield data before connecting --- packages/app/package.json | 2 +- packages/app/src/hooks/useCarbon.ts | 4 +- packages/app/src/hooks/useYieldController.ts | 43 ++++++++++++++++---- packages/client/src/index.ts | 16 +++++--- yarn.lock | 8 ++-- 5 files changed, 53 insertions(+), 20 deletions(-) diff --git a/packages/app/package.json b/packages/app/package.json index 57861622..b53984d9 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -15,7 +15,7 @@ "@solana/web3.js": "^1.66.2", "@sunrisestake/client": "*", "@sunrisestake/marinade-ts-sdk": "4.0.4-alpha.18", - "@sunrisestake/yield-controller": "^0.0.3", + "@sunrisestake/yield-controller": "^0.0.4", "@tailwindcss/forms": "^0.5.3", "@tailwindcss/typography": "^0.5.7", "@testing-library/jest-dom": "^5.16.5", diff --git a/packages/app/src/hooks/useCarbon.ts b/packages/app/src/hooks/useCarbon.ts index 9b87599f..b8dcab88 100644 --- a/packages/app/src/hooks/useCarbon.ts +++ b/packages/app/src/hooks/useCarbon.ts @@ -5,13 +5,13 @@ import { useSunriseStake } from "../context/sunriseStakeContext"; import { useYieldController } from "./useYieldController"; const useCarbon = (): { totalCarbon: number | undefined } => { - const { details, client } = useSunriseStake(); + const { details } = useSunriseStake(); const yieldControllerState = useYieldController(); const [totalCarbon, setTotalCarbon] = useState(); useEffect(() => { void (async () => { - if (details == null || client == null) return; + if (details == null || yieldControllerState == null) return; // TODO extract to some library // Total carbon is the carbon value of // 1. the extractable yield diff --git a/packages/app/src/hooks/useYieldController.ts b/packages/app/src/hooks/useYieldController.ts index 3a4e9db8..93d2f8a6 100644 --- a/packages/app/src/hooks/useYieldController.ts +++ b/packages/app/src/hooks/useYieldController.ts @@ -1,23 +1,50 @@ -import { useSunriseStake } from "../context/sunriseStakeContext"; import { useEffect, useState } from "react"; import { type YieldControllerState, YieldControllerClient, } from "@sunrisestake/yield-controller"; +import { AnchorProvider } from "@coral-xyz/anchor"; +import { useConnection } from "@solana/wallet-adapter-react"; +import { Environment } from "@sunrisestake/client"; +import { WalletAdapterNetwork } from "@solana/wallet-adapter-base"; +import { Keypair } from "@solana/web3.js"; + +const stage = + (process.env.REACT_APP_SOLANA_NETWORK as keyof typeof Environment) ?? + WalletAdapterNetwork.Devnet; export const useYieldController = (): YieldControllerState | undefined => { - const { client } = useSunriseStake(); + const { connection } = useConnection(); const [yieldState, setYieldState] = useState(); useEffect(() => { void (async () => { - if (!client) return; - const yieldControllerClient = await YieldControllerClient.get( - client.client.provider, - client.yieldControllerState + const provider = new AnchorProvider( + connection, + // we only need a read-only wallet - this allows us to get the yield status + // before the user has connected + { + publicKey: Keypair.generate().publicKey, + signAllTransactions: async (txes) => txes, + signTransaction: async (tx) => tx, + }, + {} ); - yieldControllerClient.getState().then(setYieldState).catch(console.error); + const env = Environment[stage]; + const yieldControllerClient = await YieldControllerClient.get( + provider, + env.yieldControllerState + ).catch((e) => { + console.error(e); + throw e; + }); + yieldControllerClient + .getState() + .then(setYieldState) + .catch((e) => { + console.error(e); + }); })(); - }, [client?.yieldControllerState]); + }, [connection]); return yieldState; }; diff --git a/packages/client/src/index.ts b/packages/client/src/index.ts index 3b64996f..1b2833ed 100644 --- a/packages/client/src/index.ts +++ b/packages/client/src/index.ts @@ -857,9 +857,9 @@ export class SunriseStakeClient { ): WithdrawalFees { // Calculate how much can be withdrawn from the lp (without fee) const lpSolShare = details.lpDetails.lpSolShare; - const preferredMinLiqPoolValue = new BN( - details.balances.gsolSupply.amount - ).muln(DEFAULT_LP_MIN_PROPORTION).divn(100); + const preferredMinLiqPoolValue = new BN(details.balances.gsolSupply.amount) + .muln(DEFAULT_LP_MIN_PROPORTION) + .divn(100); const postUnstakeLpSolValue = new BN(lpSolShare).sub(withdrawalLamports); // Calculate how much will be withdrawn through liquid unstaking (with fee) @@ -878,13 +878,19 @@ export class SunriseStakeClient { this.log("lp sol share: ", lpSolShare.toString()); this.log("preferred min lp value: ", preferredMinLiqPoolValue.toString()); this.log("post unstake lp sol value: ", postUnstakeLpSolValue.toString()); - this.log("amount being liquid unstaked: ", amountBeingLiquidUnstaked.toString()); + this.log( + "amount being liquid unstaked: ", + amountBeingLiquidUnstaked.toString() + ); this.log("amount to order unstake: ", amountToOrderUnstake.toString()); this.log("rent for order unstake: ", rentForOrderUnstakeTicket.toString()); const ticketFee = rentForOrderUnstakeTicket; - let totalFee = rentForOrderUnstakeTicket > 0 ? new BN(rentForOrderUnstakeTicket + 2 * NETWORK_FEE) : ZERO; + let totalFee = + rentForOrderUnstakeTicket > 0 + ? new BN(rentForOrderUnstakeTicket + 2 * NETWORK_FEE) + : ZERO; if (amountBeingLiquidUnstaked.lte(ZERO)) { return { diff --git a/yarn.lock b/yarn.lock index 9853265f..d67eb3c7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4201,10 +4201,10 @@ borsh "^0.6.0" bs58 "^5.0.0" -"@sunrisestake/yield-controller@^0.0.3": - version "0.0.3" - resolved "https://registry.yarnpkg.com/@sunrisestake/yield-controller/-/yield-controller-0.0.3.tgz#f49b334126e9e3e26dfbea7ac107d56146a5b89d" - integrity sha512-IQOSzrsZsR7QTiDamXZOP/FRoxJMulDSJ9boA4qd054/OzhtSKy6TQD2/oOX6Grp3garMk5REu594+XD65GejA== +"@sunrisestake/yield-controller@^0.0.4": + version "0.0.4" + resolved "https://registry.yarnpkg.com/@sunrisestake/yield-controller/-/yield-controller-0.0.4.tgz#04b22d658da39b78765b2644ebb77bf457f8b9e1" + integrity sha512-nY0AZKo17OhiQlK+r2u2Mnt7uch4KomSJqy5z5J43NONqG/S27JJ7FzIOKmMVtZ7mrcsANx2opWjlEFWmyuVqA== dependencies: "@coral-xyz/anchor" "^0.26.0" "@solana/spl-token" "^0.3.7"