Skip to content

Commit

Permalink
raps v2 (#6138)
Browse files Browse the repository at this point in the history
* raps

* nit

* comments

* nit

* nit

* prettier

* updates to query, types, & wallet screen components

* rm onPress

* rm logic

* thanks greg
  • Loading branch information
benisgold authored Oct 1, 2024
1 parent 1256c85 commit 794c1a6
Show file tree
Hide file tree
Showing 6 changed files with 283 additions and 0 deletions.
45 changes: 45 additions & 0 deletions src/rapsV2/actions/claimTransactionClaimableAction.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { ActionProps } from '../references';
import { sendTransaction } from '@/model/wallet';
import { getProvider } from '@/handlers/web3';
import { RainbowError } from '@/logger';
import { addNewTransaction } from '@/state/pendingTransactions';
import { NewTransaction } from '@/entities';
import { chainsName } from '@/chains';

export async function claimTransactionClaimable({ parameters, wallet }: ActionProps<'claimTransactionClaimableAction'>) {
// will uncomment actual logic in follow-up PR, don't worry about it for now
return { nonce: undefined, hash: undefined };

// const { claimTx } = parameters;

// const provider = getProvider({ chainId: claimTx.chainId });
// const result = await sendTransaction({ transaction: claimTx, existingWallet: wallet, provider });

// if (!result?.result || !!result.error || !result.result.hash) {
// throw new RainbowError('[CLAIM-TRANSACTION-CLAIMABLE]: failed to execute claim transaction');
// }

// const transaction = {
// amount: result.result.value.toString(),
// gasLimit: result.result.gasLimit,
// from: result.result.from ?? null,
// to: result.result.to ?? null,
// chainId: result.result.chainId,
// hash: result.result.hash,
// network: chainsName[result.result.chainId],
// status: 'pending',
// type: 'send',
// nonce: result.result.nonce,
// } satisfies NewTransaction;

// addNewTransaction({
// address: claimTx.from,
// chainId: claimTx.chainId,
// transaction,
// });

// return {
// nonce: result.result.nonce,
// hash: result.result.hash,
// };
}
1 change: 1 addition & 0 deletions src/rapsV2/actions/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { claimTransactionClaimable } from './claimTransactionClaimableAction';
13 changes: 13 additions & 0 deletions src/rapsV2/claimTransactionClaimableRap.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { createNewAction, createNewRap } from './common';
import { RapAction, RapParameters } from './references';

export async function createClaimTransactionClaimableRap(parameters: Extract<RapParameters, { type: 'claimTransactionClaimableRap' }>) {
let actions: RapAction<'claimTransactionClaimableAction'>[] = [];

const claim = createNewAction('claimTransactionClaimableAction', parameters.claimTransactionClaimableActionParameters);
actions = actions.concat(claim);

// create the overall rap
const newRap = createNewRap(actions);
return newRap;
}
20 changes: 20 additions & 0 deletions src/rapsV2/common.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { RapAction, RapActionParameterMap, RapActionTypes } from './references';

export interface RapActionTransaction {
hash: string | null;
}

export function createNewAction<T extends RapActionTypes>(type: T, parameters: RapActionParameterMap[T]): RapAction<T> {
const newAction = {
parameters,
transaction: { confirmed: null, hash: null },
type,
};
return newAction;
}

export function createNewRap<T extends RapActionTypes>(actions: RapAction<T>[]) {
return {
actions,
};
}
121 changes: 121 additions & 0 deletions src/rapsV2/execute.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
/* eslint-disable no-await-in-loop */
/* eslint-disable no-async-promise-executor */
/* eslint-disable no-promise-executor-return */
import { Signer } from '@ethersproject/abstract-signer';

import { RainbowError, logger } from '@/logger';

import { ActionProps, RapResponse, Rap, RapAction, RapActionResponse, RapActionTypes, RapParameters } from './references';
import { createClaimTransactionClaimableRap } from './claimTransactionClaimableRap';
import { claimTransactionClaimable } from './actions/claimTransactionClaimableAction';
import { delay } from '@/utils/delay';

// get the rap by type
export function createRap(parameters: RapParameters): Promise<{ actions: RapAction<RapActionTypes>[] }> {
switch (parameters.type) {
case 'claimTransactionClaimableRap':
return createClaimTransactionClaimableRap(parameters);
default:
return Promise.resolve({ actions: [] });
}
}

// get the action executable by type
function getActionExecutableByType<T extends RapActionTypes>(type: T, props: ActionProps<T>) {
switch (type) {
case 'claimTransactionClaimableAction':
return () => claimTransactionClaimable(props);
default:
throw new RainbowError(`[rapsV2/execute]: T - unknown action type ${type}`);
}
}

// executes a single action in the rap
// if the action executes a tx on-chain, it will return the nonce it used
// if an error occurs, we return the error message
export async function executeAction<T extends RapActionTypes>({
action,
wallet,
rap,
nonceToUse,
rapName,
}: {
action: RapAction<T>;
wallet: Signer;
rap: Rap;
nonceToUse: number | undefined;
rapName: string;
}): Promise<RapActionResponse> {
const { type, parameters } = action;
try {
const actionProps = {
wallet,
currentRap: rap,
parameters,
nonceToUse,
};
const { nonce, hash } = await getActionExecutableByType<T>(type, actionProps)();
return { nonce, errorMessage: null, hash };
} catch (error) {
logger.error(new RainbowError(`[rapsV2/execute]: ${rapName} - error execute action`), {
message: (error as Error)?.message,
});
return { nonce: null, errorMessage: String(error), hash: null };
}
}

function getRapFullName<T extends RapActionTypes>(actions: RapAction<T>[]) {
const actionTypes = actions.map(action => action.type);
return actionTypes.join(' + ');
}

const waitForNodeAck = async (hash: string, provider: Signer['provider']): Promise<void> => {
return new Promise(async resolve => {
const tx = await provider?.getTransaction(hash);
// This means the node is aware of the tx, we're good to go
if ((tx && tx.blockNumber === null) || (tx && tx?.blockNumber && tx?.blockNumber > 0)) {
resolve();
} else {
// Wait for 1 second and try again
await delay(1000);
return waitForNodeAck(hash, provider);
}
});
};

// goes through each action in the rap and executes it
// if an action executes a tx on-chain, increment the nonceToUse for the next tx
// if an action fails, it will return the error message
const executeRap = async (wallet: Signer, rap: Rap): Promise<RapResponse> => {
const { actions } = rap;
const rapName = getRapFullName(rap.actions);
let nonceToUse: number | undefined;

while (actions.length) {
const action = actions.shift();

if (!action) break;

const { nonce, errorMessage, hash } = await executeAction({
action,
wallet,
rap,
nonceToUse,
rapName,
});

if (errorMessage) return { errorMessage };

if (typeof nonce === 'number') {
actions.length >= 1 && hash && (await waitForNodeAck(hash, wallet.provider));
nonceToUse = nonce + 1;
}
}

return { errorMessage: null };
};

export async function walletExecuteRap(wallet: Signer, rapParameters: RapParameters): Promise<RapResponse> {
const rap = await createRap(rapParameters);
return executeRap(wallet, rap);
}
83 changes: 83 additions & 0 deletions src/rapsV2/references.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import { Signer } from '@ethersproject/abstract-signer';
import { TransactionRequest } from '@ethersproject/abstract-provider';

// supports legacy and new gas types
export type TransactionClaimableTxPayload = TransactionRequest &
(
| {
to: string;
from: string;
nonce: number;
gasLimit: string;
maxFeePerGas: string;
maxPriorityFeePerGas: string;
data: string;
value: '0x0';
chainId: number;
}
| {
to: string;
from: string;
nonce: number;
gasLimit: string;
gasPrice: string;
data: string;
value: '0x0';
chainId: number;
}
);

export interface ClaimTransactionClaimableActionParameters {
claimTx: TransactionClaimableTxPayload;
}

export interface RapActionTransaction {
hash: string | null;
}

export type RapActionParameterMap = {
claimTransactionClaimableAction: ClaimTransactionClaimableActionParameters;
};

export type RapParameters = {
type: 'claimTransactionClaimableRap';
claimTransactionClaimableActionParameters: ClaimTransactionClaimableActionParameters;
};

export interface RapAction<T extends RapActionTypes> {
parameters: RapActionParameterMap[T];
transaction: RapActionTransaction;
type: T;
}

export interface Rap {
actions: RapAction<'claimTransactionClaimableAction'>[];
}

export enum rapActions {
claimTransactionClaimableAction = 'claimTransactionClaimableAction',
}

export type RapActionTypes = keyof typeof rapActions;

export enum rapTypes {
claimTransactionClaimableRap = 'claimTransactionClaimableRap',
}

export type RapTypes = keyof typeof rapTypes;

export interface RapActionResponse {
nonce: number | null | undefined;
errorMessage: string | null;
hash: string | null | undefined;
}

export interface ActionProps<T extends RapActionTypes> {
nonceToUse: number | undefined;
parameters: RapActionParameterMap[T];
wallet: Signer;
}

export interface RapResponse {
errorMessage: string | null;
}

0 comments on commit 794c1a6

Please sign in to comment.