diff --git a/.gitignore b/.gitignore index a56689a2..6dc063cf 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ .DS_Store .env .idea +.gitpod.yml ./package-lock.json .vscode bin diff --git a/js/cli/src/matches.ts b/js/cli/src/matches.ts index 88047463..c52ff5d4 100755 --- a/js/cli/src/matches.ts +++ b/js/cli/src/matches.ts @@ -403,6 +403,37 @@ programCommand("drain_oracle") ); }); + programCommand("resize_oracle") + .requiredOption( + "-cp, --config-path ", + "JSON file with match settings" + ) + .action(async (files: string[], cmd) => { + const { keypair, env, configPath, rpcUrl } = cmd.opts(); + + const walletKeyPair = loadWalletKey(keypair); + const anchorProgram = await getMatchesProgram(walletKeyPair, env, rpcUrl); + + if (configPath === undefined) { + throw new Error("The configPath is undefined"); + } + const configString = fs.readFileSync(configPath); + + //@ts-ignore + const config = JSON.parse(configString); + + if (!config.resize) { + throw new Error("Must specify a new size"); + } + await anchorProgram.resizeOracle({ + authority: config.oracleState.authority + ? new web3.PublicKey(config.oracleState.authority) + : walletKeyPair.publicKey, + seed: config.oracleState.seed, + resize: new BN(config.resize), + }); + }); + programCommand("create_or_update_oracle") .requiredOption( "-cp, --config-path ", diff --git a/js/lib/src/contract/matches.ts b/js/lib/src/contract/matches.ts index e8d5a839..9fb1302f 100644 --- a/js/lib/src/contract/matches.ts +++ b/js/lib/src/contract/matches.ts @@ -119,7 +119,11 @@ export interface CreateMatchAdditionalArgs { tokenTransferRoot: null; tokenTransfers: null | AnchorTokenDelta[]; } - +export interface ResizeOracleArgs { + seed: string; + authority: web3.PublicKey; + resize: BN; +} export interface CreateOrUpdateOracleArgs { seed: string; authority: web3.PublicKey; @@ -538,6 +542,35 @@ export class MatchesInstruction { signers: [], }; } + + async resizeOracle( + args: ResizeOracleArgs, + ) { + const [oracle, _oracleBump] = await getOracle( + new web3.PublicKey(args.seed), + args.authority + ); + + const match = (await getMatch(oracle))[0]; + + return { + instructions: [ + await this.program.methods + .resizeOracle({ + ...args, + }) + .accounts({ + oracle, + matchInstance: match, + payer: (this.program.provider as AnchorProvider).wallet.publicKey, + systemProgram: SystemProgram.programId, + rent: web3.SYSVAR_RENT_PUBKEY, + }) + .instruction(), + ], + signers: [], + }; + } } export class MatchesProgram { @@ -745,6 +778,22 @@ export class MatchesProgram { signers ); } + + async resizeOracle( + args: ResizeOracleArgs, + _accounts = {}, + _additionalArgs = {} + ) { + const { instructions, signers } = + await this.instruction.resizeOracle(args); + + await sendTransactionWithRetry( + (this.program.provider as AnchorProvider).connection, + (this.program.provider as AnchorProvider).wallet, + instructions, + signers + ); + } } export async function getMatchesProgram( diff --git a/rust/matches/src/lib.rs b/rust/matches/src/lib.rs index c0666a62..f512cd09 100644 --- a/rust/matches/src/lib.rs +++ b/rust/matches/src/lib.rs @@ -35,6 +35,10 @@ pub struct CreateOrUpdateOracleArgs { finalized: bool, } +#[derive(AnchorSerialize, AnchorDeserialize, Clone)] +pub struct ResizeOracleArgs { + resize: u64, +} #[derive(AnchorSerialize, AnchorDeserialize, Clone)] pub struct DrainOracleArgs { seed: Pubkey, @@ -121,11 +125,43 @@ pub mod matches { win_oracle.finalized = finalized; win_oracle.token_transfer_root = token_transfer_root.clone(); + win_oracle.token_transfers = token_transfers.clone(); return Ok(()); } + pub fn resize_oracle<'a, 'b, 'c, 'info>( + ctx: Context<'a, 'b, 'c, 'info, ResizeOracle<'info>>, + args: ResizeOracleArgs, + ) -> Result<()> { + + let win_oracle_account = &mut ctx.accounts.win_oracle.to_account_info(); + + let system_program = &ctx.accounts.system_program; + let payer_account = &ctx.accounts.payer.to_account_info(); + let new_size = args.resize as usize; + if new_size > win_oracle_account.data.borrow().len() { + + + let rent = Rent::get()?; + let new_minimum_balance = rent.minimum_balance(new_size); + + let lamports_diff = new_minimum_balance.saturating_sub(win_oracle_account.lamports()); + invoke( + &system_instruction::transfer(payer_account.key, win_oracle_account.key, lamports_diff), + &[ + payer_account.clone(), + win_oracle_account.clone(), + system_program.to_account_info().clone(), + ], + )?; + win_oracle_account.realloc(new_size, false)?; + } + + Ok(()) + } + pub fn create_match<'a, 'b, 'c, 'info>( ctx: Context<'a, 'b, 'c, 'info, CreateMatch<'info>>, args: CreateMatchArgs, @@ -755,6 +791,18 @@ pub struct LeaveMatch<'info> { token_program: Program<'info, Token>, } +#[derive(Accounts)] +#[instruction(args: ResizeOracleArgs)] +pub struct ResizeOracle<'info> { + #[account(address == match_instance.win_oracle)] + win_oracle: UncheckedAccount<'info>, + #[account(mut, seeds=[PREFIX.as_bytes(), match_instance.win_oracle.as_ref()], bump=match_instance.bump)] + match_instance: Account<'info, Match>, + #[account(mut)] + payer: Signer<'info>, + system_program: Program<'info, System>, + rent: Sysvar<'info, Rent>, +} /// While not required to be an account owned by this program, we provide an easy /// set of endpoitns to create oracles using the program if you don't want to do it yourself. #[derive(Accounts)]