-
Notifications
You must be signed in to change notification settings - Fork 635
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
281 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
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'>) { | ||
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, | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export { claimTransactionClaimable } from './claimTransactionClaimableAction'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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, | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} |