diff --git a/README.md b/README.md index fbb9cda..7127bbc 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ projects, controlled by a DAO. ## History -### 1. Initial release - Toucan NCT Buy-Burn-Fixxed +### 1. Initial release - Toucan NCT Buy-Burn-Fixed The initial version of the Sunrise Yield Controller used the Buy-Burn-Fixed strategy to distribute yield. This strategy was implemented in the `buy-burn-fixed` package. diff --git a/packages/tests/yieldRouter.ts b/packages/tests/yieldRouter.ts index f93387b..65afcb7 100644 --- a/packages/tests/yieldRouter.ts +++ b/packages/tests/yieldRouter.ts @@ -29,9 +29,19 @@ describe("yield-router", () => { context("create and update", () => { it("can register a new yield router state", async () => { - const outputYieldAccounts = [Keypair.generate().publicKey]; - const spendProportions = [100]; + // setup + // where is the input? + const outputYieldAccounts = [ + // create a new random public key + PublicKey.unique() + ]; + const spendProportions = [ + // 100% of the yield is sent to this address + 100 + ]; + + // act client = await YieldRouterClient.register( sunriseState, authority.publicKey, @@ -39,14 +49,24 @@ describe("yield-router", () => { spendProportions, spendThreshold ); + + // assert + // ??? }); it("should be updateable by the admin", async () => { + // setup const outputYieldAccounts = [PublicKey.unique(), PublicKey.unique()]; const proportions = [30, 70]; + + // act + // the client is using the admin key await client.updateOutputYieldAccounts(outputYieldAccounts, proportions); - const retrieved = await YieldRouterClient.fetch(client.stateAddress); + // assert + // get a new client instance + const yieldRouterStateAddress = client.stateAddress; + const retrieved = await YieldRouterClient.fetch(yieldRouterStateAddress); expect(retrieved.config?.outputYieldAccounts).to.deep.equal( outputYieldAccounts @@ -55,20 +75,24 @@ describe("yield-router", () => { }); it("should not be updateable by others", async () => { - const anotherUser = Keypair.generate(); - const wallet = new Wallet(anotherUser); + // setup + // create a new user and give them some funds (so they can send transactions) + const unauthorisedUser = Keypair.generate(); + const unauthorisedUserWallet = new Wallet(unauthorisedUser); const connection = client.program.provider.connection; const tx = await connection.requestAirdrop( - anotherUser.publicKey, + unauthorisedUser.publicKey, LAMPORTS_PER_SOL ); const blockhash = await connection.getLatestBlockhash(); await connection.confirmTransaction({ signature: tx, ...blockhash }); - const provider = new AnchorProvider(connection, wallet, {}); + // now the user has funds + // create a new yield router client for that user + const unauthorisedUserProvider = new AnchorProvider(connection, unauthorisedUserWallet, {}); const unauthorisedClient = await YieldRouterClient.fetch( client.stateAddress, - provider + unauthorisedUserProvider ); const shouldFail = unauthorisedClient.updateOutputYieldAccounts( @@ -79,6 +103,7 @@ describe("yield-router", () => { return expect(shouldFail).to.be.rejectedWith("Unauthorized."); }); +<<<<<<< HEAD it("should be able to update with a new update authority", async () => { // Setup // Generate a new keypair that is going to be the new update authority @@ -128,6 +153,22 @@ describe("yield-router", () => { outputYieldAccounts ); expect(retrieved.config?.spendProportions).to.deep.equal(proportions); +======= + it('should ...', () => { + const newKey = Keypair.generate(); + + const transaction = new Transaction().add( + SystemProgram.transfer({ + fromPubkey: newKey.publicKey, + toPubkey: authority.publicKey, + lamports: 100_000, + }) + ) + + transaction.sign(newKey); + + console.log(transaction.signatures); +>>>>>>> 4b651c3 (Add code documentation) }); }); diff --git a/packages/yield-router/client/index.ts b/packages/yield-router/client/index.ts index fcb5e1a..81eeea6 100644 --- a/packages/yield-router/client/index.ts +++ b/packages/yield-router/client/index.ts @@ -74,6 +74,7 @@ export class YieldRouterClient { } private async init(): Promise { + // ask anchor to get the contents of the account at the stateAddress const state = await this.program.account.state.fetch(this.stateAddress); this.config = { @@ -103,10 +104,12 @@ export class YieldRouterClient { stateAddress: PublicKey, provider?: AnchorProvider ): Promise { + // constructor is synchronous and does not retrieve information from the blockchain const client = new YieldRouterClient( provider ?? setUpAnchor(), stateAddress ); + // retrieve the state from the chain await client.init(); if (!client.config) { @@ -115,16 +118,25 @@ export class YieldRouterClient { return client as InitialisedClient; } + /** + * Register a new yield router state on chain. + * This will typically happen only once. + * @param sunriseState + * @param updateAuthority + * @param outputYieldAccounts + * @param spendProportions + * @param spendThreshold + */ public static async register( - sunriseState: PublicKey, + sunriseState: PublicKey, // (0) updateAuthority: PublicKey, outputYieldAccounts: PublicKey[], spendProportions: number[], spendThreshold: BN ): Promise { - // find state address - const stateAddress = - await YieldRouterClient.getStateAddressFromSunriseAddress(sunriseState); + // find state address PDA (1) + const stateAddress = await YieldRouterClient.getStateAddressFromSunriseAddress(sunriseState); + // get input yield account PDA (2) const inputYieldAccount = getInputYieldAccountForState(stateAddress); const client = new YieldRouterClient(setUpAnchor(), stateAddress); @@ -133,6 +145,7 @@ export class YieldRouterClient { payer: client.provider.wallet.publicKey, state: stateAddress, inputYieldAccount, + // system program is used because we are instantiating a new account systemProgram: SystemProgram.programId, }; @@ -157,6 +170,7 @@ export class YieldRouterClient { throw e; }); + // now that the state is registered on chain, we can hydrate the client instance with its data await client.init(); return client as InitialisedClient; @@ -172,6 +186,7 @@ export class YieldRouterClient { const accounts = { payer: this.provider.wallet.publicKey, state: this.stateAddress, + // System program is needed because we may be resizing the state account systemProgram: SystemProgram.programId, }; diff --git a/programs/yield-router/src/utils/state.rs b/programs/yield-router/src/utils/state.rs index e03a44e..83f2240 100644 --- a/programs/yield-router/src/utils/state.rs +++ b/programs/yield-router/src/utils/state.rs @@ -66,6 +66,7 @@ pub struct UpdateState<'info> { #[account( mut, constraint = state.update_authority == payer.key() @ ErrorCode::Unauthorized, + // resize the state account if necessary realloc = State::space(state_in.output_yield_accounts.len() as u8), realloc::payer = payer, realloc::zero = false,