Skip to content

Commit

Permalink
Return meaningful result from transport
Browse files Browse the repository at this point in the history
  • Loading branch information
DogLooksGood committed Oct 12, 2023
1 parent 1fbb1be commit 3a9c170
Show file tree
Hide file tree
Showing 12 changed files with 146 additions and 76 deletions.
4 changes: 4 additions & 0 deletions api/src/effect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,10 @@ impl Effect {
}
}

pub fn __set_checkpoint_raw(&mut self, raw: Vec<u8>) {
self.checkpoint = Some(raw);
}

pub fn __checkpoint(&mut self) -> Option<Vec<u8>> {
std::mem::replace(&mut self.checkpoint, None)
}
Expand Down
2 changes: 1 addition & 1 deletion api/src/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ impl Default for InitAccount {
}

pub trait GameHandler: Sized + BorshSerialize + BorshDeserialize {
type Checkpoint: BorshDeserialize + BorshDeserialize;
type Checkpoint: BorshSerialize + BorshDeserialize;

/// Initialize handler state with on-chain game account data.
fn init_state(effect: &mut Effect, init_account: InitAccount) -> HandleResult<Self>;
Expand Down
10 changes: 3 additions & 7 deletions js/sdk-core/src/app-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
} from './connection';
import { GameContext } from './game-context';
import { GameContextSnapshot } from './game-context-snapshot';
import { ITransport } from './transport';
import { ITransport, TransactionResult } from './transport';
import { IWallet } from './wallet';
import { Handler, InitAccount } from './handler';
import { Encryptor, IEncryptor } from './encryptor';
Expand Down Expand Up @@ -81,7 +81,6 @@ export class AppClient {
#profileCaches: ProfileCache;
#info: GameInfo;
#decryptionCache: DecryptionCache;
#storage?: IStorage;

constructor(
gameAddr: string,
Expand All @@ -98,7 +97,6 @@ export class AppClient {
encryptor: IEncryptor,
info: GameInfo,
decryptionCache: DecryptionCache,
storage?: IStorage,
) {
this.#gameAddr = gameAddr;
this.#handler = handler;
Expand All @@ -115,7 +113,6 @@ export class AppClient {
this.#profileCaches = new ProfileCache(transport);
this.#info = info;
this.#decryptionCache = decryptionCache;
this.#storage = storage;
}

static async initialize(opts: AppClientInitOpts): Promise<AppClient> {
Expand Down Expand Up @@ -204,7 +201,6 @@ export class AppClient {
encryptor,
info,
decryptionCache,
storage,
);
} finally {
console.groupEnd();
Expand Down Expand Up @@ -348,7 +344,7 @@ export class AppClient {
/**
* Join game.
*/
async join(params: JoinOpts) {
async join(params: JoinOpts): Promise<TransactionResult<void>> {
const gameAccount = await this.#transport.getGameAccount(this.gameAddr);
if (gameAccount === undefined) {
throw new Error('Game account not found');
Expand All @@ -372,7 +368,7 @@ export class AppClient {

const publicKey = await this.#encryptor.exportPublicKey();

this.#transport.join(this.#wallet, {
return await this.#transport.join(this.#wallet, {
gameAddr: this.gameAddr,
amount: params.amount,
accessVersion: gameAccount.accessVersion,
Expand Down
32 changes: 21 additions & 11 deletions js/sdk-core/src/app-helper.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { EntryTypeCash, GameAccount, GameRegistration, INft, IToken, PlayerProfile, TokenWithBalance } from './accounts';
import { IStorage, getTtlCache, setTtlCache } from './storage';
import { CreateGameAccountParams, ITransport } from './transport';
import { EntryTypeCash, GameAccount, INft, IToken, PlayerProfile, TokenWithBalance } from './accounts';
import { IStorage } from './storage';
import { CreateGameAccountParams, ITransport, TransactionResult } from './transport';
import { IWallet } from './wallet';


Expand Down Expand Up @@ -48,7 +48,7 @@ export class AppHelper {
* @param params - Parameters for game creation
* @returns The address of created game
*/
async createGame(wallet: IWallet, params: CreateGameAccountParams): Promise<string> {
async createGame(wallet: IWallet, params: CreateGameAccountParams): Promise<TransactionResult<string>> {
if (params.title.length == 0 || params.title.length > 16) {
throw new Error('Invalid title');
}
Expand All @@ -69,9 +69,13 @@ export class AppHelper {
throw new Error('Invalid maxPlayers');
}

let addr = await this.#transport.createGameAccount(wallet, params);
console.debug('Game account created at %s', addr);
return addr;
let res = await this.#transport.createGameAccount(wallet, params);
if (res.result === 'ok') {
console.debug('Game account created at %s', res.value);
} else {
console.error('Failed to create game account');
}
return res;
}

/**
Expand All @@ -81,8 +85,8 @@ export class AppHelper {
* @param gameAddr - The address of game account.
* @param regAddr - The address of registration account.
*/
async registerGame(wallet: IWallet, gameAddr: string, regAddr: string) {
await this.#transport.registerGame(wallet, {
async registerGame(wallet: IWallet, gameAddr: string, regAddr: string): Promise<TransactionResult<void>> {
return await this.#transport.registerGame(wallet, {
gameAddr,
regAddr,
});
Expand All @@ -95,11 +99,17 @@ export class AppHelper {
* @param nick - The nick name
* @param pfp - The address of avatar NFT
*/
async createProfile(wallet: IWallet, nick: string, pfp: string | undefined) {
await this.#transport.createPlayerProfile(wallet, {
async createProfile(wallet: IWallet, nick: string, pfp: string | undefined): Promise<TransactionResult<void>> {
const res = await this.#transport.createPlayerProfile(wallet, {
nick,
pfp,
});
if (res.result === 'ok') {
console.debug('Created player profile');
} else {
console.error('Failed to create player profile');
}
return res;
}

/**
Expand Down
38 changes: 26 additions & 12 deletions js/sdk-core/src/transport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,26 @@ import {
INft,
IToken,
RegistrationWithGames,
Token,
RecipientAccount,
EntryType,
} from './accounts';
import { IStorage } from './storage';

export type TransactionResultSuccess<T> = T extends void ? { result: 'ok', } : { result: 'ok', value: T };

export type TransactionResult<T> =
| TransactionResultSuccess<T>
| {
result: 'rejected'
}
| {
result: 'insufficient-funds'
}
| {
result: 'err',
error: string
};

export type CreateGameAccountParams = {
title: string;
bundleAddr: string;
Expand Down Expand Up @@ -83,25 +97,25 @@ export type RecipientClaimParams = {
export interface ITransport {
get chain(): string

createGameAccount(wallet: IWallet, params: CreateGameAccountParams): Promise<string>;
createGameAccount(wallet: IWallet, params: CreateGameAccountParams): Promise<TransactionResult<string>>;

closeGameAccount(wallet: IWallet, params: CloseGameAccountParams): Promise<void>;
closeGameAccount(wallet: IWallet, params: CloseGameAccountParams): Promise<TransactionResult<void>>;

join(wallet: IWallet, params: JoinParams): Promise<void>;
join(wallet: IWallet, params: JoinParams): Promise<TransactionResult<void>>;

deposit(wallet: IWallet, params: DepositParams): Promise<void>;
deposit(wallet: IWallet, params: DepositParams): Promise<TransactionResult<void>>;

vote(wallet: IWallet, params: VoteParams): Promise<void>;
vote(wallet: IWallet, params: VoteParams): Promise<TransactionResult<void>>;

createPlayerProfile(wallet: IWallet, params: CreatePlayerProfileParams): Promise<void>;
createPlayerProfile(wallet: IWallet, params: CreatePlayerProfileParams): Promise<TransactionResult<void>>;

publishGame(wallet: IWallet, params: PublishGameParams): Promise<string>;
publishGame(wallet: IWallet, params: PublishGameParams): Promise<TransactionResult<string>>;

createRegistration(wallet: IWallet, params: CreateRegistrationParams): Promise<string>;
createRegistration(wallet: IWallet, params: CreateRegistrationParams): Promise<TransactionResult<string>>;

registerGame(wallet: IWallet, params: RegisterGameParams): Promise<void>;
registerGame(wallet: IWallet, params: RegisterGameParams): Promise<TransactionResult<void>>;

unregisterGame(wallet: IWallet, params: UnregisterGameParams): Promise<void>;
unregisterGame(wallet: IWallet, params: UnregisterGameParams): Promise<TransactionResult<void>>;

getGameAccount(addr: string): Promise<GameAccount | undefined>;

Expand All @@ -127,5 +141,5 @@ export interface ITransport {

fetchBalances(walletAddr: string, tokenAddrs: string[]): Promise<Map<string, bigint>>;

recipientClaim(wallet: IWallet, params: RecipientClaimParams): Promise<void>;
recipientClaim(wallet: IWallet, params: RecipientClaimParams): Promise<TransactionResult<void>>;
}
4 changes: 3 additions & 1 deletion js/sdk-core/src/wallet.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { TransactionResult } from "./transport";

export interface IWallet {
isConnected: boolean;
walletAddr: string;
sendTransaction(tx: any, conn: any): Promise<void>;
sendTransaction(tx: any, conn: any): Promise<TransactionResult<void>>;
}
27 changes: 15 additions & 12 deletions js/sdk-facade/src/facade-transport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
UnregisterGameParams,
VoteParams,
IStorage,
TransactionResult,
} from '@race-foundation/sdk-core';
import { deserialize } from '@race-foundation/borsh';

Expand Down Expand Up @@ -98,52 +99,54 @@ export class FacadeTransport implements ITransport {
return 'Facade'
}

async createGameAccount(wallet: IWallet, params: CreateGameAccountParams): Promise<string> {
async createGameAccount(wallet: IWallet, params: CreateGameAccountParams): Promise<TransactionResult<string>> {
const walletAddr = wallet.walletAddr;
const gameAddr = makeid(16);
const data = [...params.data];
const ix: CreateGameAccountInstruction = { walletAddr, gameAddr, ...params, ...params.entryType, data };
await this.sendInstruction('create_account', ix);
return gameAddr;
return { result: 'ok', value: gameAddr };
}
closeGameAccount(wallet: IWallet, params: CloseGameAccountParams): Promise<void> {
closeGameAccount(wallet: IWallet, params: CloseGameAccountParams): Promise<TransactionResult<void>> {
throw new Error('Method not implemented.');
}
deposit(wallet: IWallet, params: DepositParams): Promise<void> {
deposit(wallet: IWallet, params: DepositParams): Promise<TransactionResult<void>> {
throw new Error('Method not implemented.');
}
vote(wallet: IWallet, params: VoteParams): Promise<void> {
vote(wallet: IWallet, params: VoteParams): Promise<TransactionResult<void>> {
throw new Error('Method not implemented.');
}
publishGame(wallet: IWallet, params: PublishGameParams): Promise<string> {
publishGame(wallet: IWallet, params: PublishGameParams): Promise<TransactionResult<string>> {
throw new Error('Method not implemented.');
}
createRegistration(wallet: IWallet, params: CreateRegistrationParams): Promise<string> {
createRegistration(wallet: IWallet, params: CreateRegistrationParams): Promise<TransactionResult<string>> {
throw new Error('Method not implemented.');
}
registerGame(wallet: IWallet, params: RegisterGameParams): Promise<void> {
registerGame(wallet: IWallet, params: RegisterGameParams): Promise<TransactionResult<void>> {
throw new Error('Method not implemented.');
}
unregisterGame(wallet: IWallet, params: UnregisterGameParams): Promise<void> {
unregisterGame(wallet: IWallet, params: UnregisterGameParams): Promise<TransactionResult<void>> {
throw new Error('Method not implemented.');
}
recipientClaim(_wallet: IWallet, _params: RecipientClaimParams): Promise<void> {
recipientClaim(_wallet: IWallet, _params: RecipientClaimParams): Promise<TransactionResult<void>> {
throw new Error('Method not implemented.');
}

async listTokens(_storage?: IStorage): Promise<IToken[]> {
return Object.values(tokenMap);
}

async createPlayerProfile(wallet: IWallet, params: CreatePlayerProfileParams): Promise<void> {
async createPlayerProfile(wallet: IWallet, params: CreatePlayerProfileParams): Promise<TransactionResult<void>> {
const playerAddr = wallet.walletAddr;
const ix: CreatePlayerProfileInstruction = { playerAddr, ...params };
await this.sendInstruction('create_profile', ix);
return { result: 'ok' };
}
async join(wallet: IWallet, params: JoinParams): Promise<void> {
async join(wallet: IWallet, params: JoinParams): Promise<TransactionResult<void>> {
const playerAddr = wallet.walletAddr;
const ix: JoinInstruction = { playerAddr, ...params };
await this.sendInstruction('join', ix);
return { result: 'ok' };
}
async getGameAccount(addr: string): Promise<GameAccount | undefined> {
const data: Uint8Array | undefined = await this.fetchState('get_account_info', [addr]);
Expand Down
4 changes: 2 additions & 2 deletions js/sdk-facade/src/facade-wallet.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { IWallet } from '@race-foundation/sdk-core';
import { IWallet, TransactionResult } from '@race-foundation/sdk-core';
import { makeid } from './utils';

export class FacadeWallet implements IWallet {
Expand All @@ -22,7 +22,7 @@ export class FacadeWallet implements IWallet {
return this.#addr;
}

sendTransaction(_tx: any, _conn: any): Promise<void> {
sendTransaction(_tx: any, _conn: any): Promise<TransactionResult<void>> {
throw new Error('Method not implemented.');
}
}
18 changes: 13 additions & 5 deletions js/sdk-solana/src/phantom-wallet.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Connection, PublicKey, TransactionInstruction } from '@solana/web3.js';
import { IWallet } from '@race-foundation/sdk-core';
import { Connection, TransactionInstruction } from '@solana/web3.js';
import { IWallet, TransactionResult } from '@race-foundation/sdk-core';

const getProvider = () => {
if ('phantom' in window) {
Expand Down Expand Up @@ -39,16 +39,24 @@ export class PhantomWalletAdapter implements IWallet {
this.#skipPreflight = opts.skipPreflight;
}

async sendTransaction(tx: TransactionInstruction, conn: Connection): Promise<void> {
async sendTransaction(tx: TransactionInstruction, conn: Connection): Promise<TransactionResult<void>> {
const {
context: { slot: minContextSlot },
value: { blockhash, lastValidBlockHeight },
} = await conn.getLatestBlockhashAndContext();
const signedTransaction = await this.#provider.signTransaction(tx);
const signature = await conn.sendRawTransaction(signedTransaction.serialize(), {
skipPreflight: this.#skipPreflight,
});
await conn.confirmTransaction({ blockhash, lastValidBlockHeight, signature });
const resp = await conn.confirmTransaction({ blockhash, lastValidBlockHeight, signature });
if (resp.value.err !== null) {
return {
result: 'err', error: resp.value.err.toString()
}
} else {
return {
result: 'ok'
}
}
}

async connect(): Promise<void> {
Expand Down
Loading

0 comments on commit 3a9c170

Please sign in to comment.